#!/bin/bash
. /etc/profile 1>/dev/null 2>&1 #load env
. ~/.bash_profile 1>/dev/null 2>&1
BASE_PATH=`dirname $0`
[ "$BASE_PATH" = "." ] && BASE_PATH=`pwd`
BASE_NAME=$(basename $0)
BASE_SHORT_NAME=$(echo $BASE_NAME|awk -F"." '{print $1}')
CONF_FILE=$BASE_PATH/$BASE_SHORT_NAME.conf
CURR_DATE=$(date '+%Y%m%d')
PID_FILE=$BASE_PATH/$BASE_SHORT_NAME.pid
LOG_DIR=$BASE_PATH/log #the log save path
ANDROID_ROOT= #the code root path
EXTRA_CMD_BEFORE=
EXTRA_CMD_AFTER=
DIFF_SAVE=$LOG_DIR/changes #the local change save directory
MAX_OUT_DAYS=1 #the max out save days
CRONTAB_HOUR=2 #
CRONTAB_MINUTE=30 #
REPO_SYNC=1
TEST_MODE=0 #debug
DEFAULT_COMBO_LIST="zhimo_minjava_riscv64-eng aosp_x86_64-eng aosp_arm64-eng" #the combo which need to compile
COMBO_LIST=""
### quick depoy the script
## curl -s https://gitee.com/ls3/es_tools/raw/master/auto_build.sh > auto_build.sh && chmod +x auto_build.sh && ./auto_build.sh -i
###
UPDATE_URL="https://gitee.com/ls3/es_tools/raw/master/${BASE_NAME}" #update url for current scripts
CPU_CORES=$(cat /proc/cpuinfo |grep processor|wc -l) #the cpu core numbers
pid=""
START_FLAG=0
CLEAN_OUT_DIRECTORY=0
[ ! -d $LOG_DIR ] && mkdir -p $LOG_DIR
[ ! -d $DIFF_SAVE ] && mkdir -p $DIFF_SAVE

show_help()
{
    RET=$?
cat<<EOF
Auto build aosp target V1.0
It backup all changes and repo sync to lastest code, then it start to
compile with different combo ["$COMBO_LIST"]
Usage:
$BASE_SHORT_NAME [options]
Options:
  -i, install             install script to crontab, default at $CRONTAB_HOUR:$CRONTAB_MINUTE
      -hour=<value>       start hour for crontab, range (0..23)
      -minute=<value>     start minute for crontab, range (0..59)
  -h, -help               show  help
  -u, update              update script if found the new version
  -conf=<config_file>     default config file was $BASE_SHORT_NAME.conf
  stop                    stop $BASE_SHORT_NAME if it was running
  status                  query running state
  start                   start $BASE_SHORT_NAME
  -c, --clean-out         delete the out directory before compile
  --remain-cpu-cores=<value>
                          keep cpu cores not used. compile task would used max-cpu-cores - remain
  --nosync                it don't need repo sync. Default would sync lastest code
Example:
  `basename $0` install -hour=$CRONTAB_HOUR -minute=$CRONTAB_MINUTE #install the script to crontab,
      The task would start at $CRONTAB_HOUR:$CRONTAB_MINUTE every day
  `basename $0` update                     #check and update current script

EOF
    exit $RET
}

read_value_from_conf(){
    name="$1"
    value=""
    if [ -f $CONF_FILE ];then
        value=$(cat $CONF_FILE|awk -F"${name}="|awk '{print $2}'|grep -o "[^ \t]\+\( \+[^ \t]\+\)*")
    fi
    echo "$value"
}

read_android_root_from_conf()
{
    read_value_from_conf ANDROID_ROOT
}

read_combo_list_from_conf()
{
    [ -f $CONF_FILE ] && COMBO_LIST_TMP=$(cat $CONF_FILE|grep COMBO_LIST|awk -F"=" '{print $2}'|awk '{for(i=1;i<=NF;i++){print $i}}'|tr '\n' " ") && [ -n "$COMBO_LIST_TMP" ] && COMBO_LIST=$COMBO_LIST_TMP
}

read_extra_cmd_before_from_conf()
{
    read_value_from_conf EXTRA_CMD_BEFORE
}

read_extra_cmd_after_from_conf()
{
    read_value_from_conf EXTRA_CMD_AFTER
}

refresh_conf()
{
    read_android_root_from_conf
    read_combo_list_from_conf
    read_extra_cmd_before_from_conf
    read_extra_cmd_after_from_conf
}

###
try_find_android_root()
{
    newest_dir=""
    for find_dir in / $HOME
    do
        find_res=$(find $find_dir -maxdepth 3 -type d 2>/dev/null|grep -v '/\.'|sort)
        while read dir
        do
            if [ -f "$dir/build/envsetup.sh" ];then
                [ -z "$newest_dir" ] && newest_dir=$dir && continue
                [ "$newest_dir" -ot "$dir" ] && newest_dir=$dir
            fi
        done<<EOF
$find_res
EOF
    done
    echo "$newest_dir"
    [ -n "$newest_dir" ] && return 0 || return 1
}

###install the script to crontab
install_crontab()
{
    log_str "INFO: install crontab ... begin"
    if [ -z "$ANDROID_ROOT" ] ;then
        log_str "INFO: try to find android root path ..."
        find_root=$(try_find_android_root)
        [ $? -eq 0 ] && log_str "INFO: $find_root" || log_str "WARN: not found android root path"
    else
        find_root=$ANDROID_ROOT
    fi
    log_str "INFO: save config ..."
    echo "ANDROID_ROOT=$find_root" > $CONF_FILE
    [ -z "$COMBO_LIST" ] && COMBO_LIST=$DEFAULT_COMBO_LIST
    echo "COMBO_LIST=$COMBO_LIST" >> $CONF_FILE
    echo "EXTRA_CMD_BEFORE=" >> $CONF_FILE
    echo "EXTRA_CMD_AFTER=" >> $CONF_FILE
    [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
    crontab -l |grep -vw $BASE_SHORT_NAME > ~/crontab.tmp
    echo "$CRONTAB_MINUTE $CRONTAB_HOUR * * * $BASE_PATH/$BASE_NAME -c -start -conf=`basename $CONF_FILE` 1>/dev/null 2>&1" >> ~/crontab.tmp
    log_str "INFO: load origin crontab ..."
    crontab ~/crontab.tmp
    [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
    rm ~/crontab.tmp
    log_str "INFO: install $BASE_SHORT_NAME task at $CRONTAB_HOUR:$CRONTAB_MINUTE to crontab ... finish"
    exit 0
}

###record the log
log_str()
{
    echo "`date '+%F %T'` $@" |tee -a $LOG_DIR/${CURR_DATE}_${BASE_SHORT_NAME}.log
    echo ""
}

###query_status
query_status_from_log()
{
    if [ ! -e $PID_FILE ];then
        return
    fi
    find $LOG_DIR -name "*.log" -cnewer $PID_FILE|grep -v $BASE_SHORT_NAME|grep -v repo_sync|while read log_file
    do
        name=$(basename $log_file|awk -F".log" '{print $1}')
        echo "$name : "
        tail -100 $log_file|egrep '^\['|tail -1
        tail -50 $log_file|egrep '^###|### build'|tail -1
        [ $? -eq 0 ] && echo
    done
}

###run the command
Run()
{
   log_str "Run $@"
   $@
   if [ $? -ne 0 ];then
       log_str "ERR : [$@]"
   fi
}

###update the current script
update_script()
{
    origin=$BASE_PATH/$BASE_NAME
    tmp=${origin}.tmp
    #wget --quiet -o $BASE_PATH/$BASE_NAME.tmp $UPDATE_URL
    curl -s "$UPDATE_URL" > $tmp
    if [ $? -eq 0 ];then
        if cmp -s $tmp $origin; then
            #file was same
            log_str "INFO: already lastest version"
            rm -f $tmp
        else
            log_str "INFO: found new version"
            mv -f $origin $origin.$CURR_DATE
            mv -f $tmp $origin
            chmod +x $origin
            log_str "INFO: update success"
        fi
    else
        log_str "WARN: can not connect update url"
    fi
}

#check the process whether was alive
check_pid_alive()
{
    RET=$?
    if kill -0 $pid 1>/dev/null 2>/dev/null;then
        RET=0
    else
        log_str "WARN: The parent script already exit, so exit..."
        exit 0
    fi
}

read_pid_alive()
{
    RET=1
    if [ -e $PID_FILE ];then
        pid=$(cat $PID_FILE)
        if kill -0 $pid 1>/dev/null 2>/dev/null;then
            RET=0
        fi
    fi
    return $RET
}
#echo $#

INSTALL_FLAG=0
while test $# -gt 0; do
    #echo "$1"
    case "$1" in
    -i|*install)
        INSTALL_FLAG=1
        shift
        ;;
    -h|-help|--help)
        refresh_conf
        show_help
        ;;
    -d|-debug|--debug)
        TEST_MODE=1
        set -x
        shift
        ;;
    --nosync)
        REPO_SYNC=0
        shift
        ;;
    -conf=*)
        CONF_FILE=$(echo "$1"|awk '{print substr($0,7)}')
        tmp_dir_name=$(dirname $CONF_FILE)
        [ "$tmp_dir_name" = "." ] && CONF_FILE=$BASE_PATH/$CONF_FILE
        shift
        ;;
    -u|*update)
        update_script
        exit 0
        ;;
    -hour=[0-9]|-hour=1[0-9]|-hour=2[0-3])
        CRONTAB_HOUR=$(echo "$1"|awk '{print substr($0,7)}')
        shift
        ;;
    -minute=[0-9]|-minute=[0-5][0-9])
        CRONTAB_MINUTE=$(echo "$1"|awk '{print substr($0,9)}')
        shift
        ;;
    --remain-cpu-cores=[1-9]|--remain-cpu-cores=[1-9][0-9])
        remain_cores=$(echo "$1"|grep -oP '\d+')
        CPU_CORES=`expr $CPU_CORES - $remain_cores`
        [ $CPU_CORES -lt 1 ] && CPU_CORES=1
        log_str "INFO: set max cpu cores=$CPU_CORES"
        shift
        ;;
    *stop)
        read_pid_alive
        if [ $? -eq 0 ]; then
            ps -fe|grep $pid|grep make|grep -v grep|awk '{if($3=='$pid') {print $2}}'|xargs kill
            kill -9 $pid
        else
            log_str "INFO: the $BASE_SHORT_NAME was stopped"
        fi
        exit 0
        ;;
    *status)
        read_pid_alive
        if [ $? -eq 0 ]; then
            log_str "INFO: $BASE_SHORT_NAME was running[$pid]"
        else
            log_str "INFO: $BASE_SHORT_NAME was stopped"
        fi
        query_status_from_log
        exit 0
        ;;
    *start)
        START_FLAG=1
        shift
        ;;
    -c|--clean-out)
        CLEAN_OUT_DIRECTORY=1
        shift
        ;;
    -*)
        echo "unrecognized option $1" >&2
        echo "`basename $0` --help for more information." >&2
        exit 0
        ;;
    *)
        break
        ;;
    esac
done
refresh_conf

if [ $INSTALL_FLAG -eq 1 ]; then
    install_crontab
fi

if [ -z "$ANDROID_ROOT" ] || [ ! -d $ANDROID_ROOT ];then
    log_str "ERR : haven't set the android root path in $CONF_FILE"
    exit 0
fi

read_pid_alive
if [ $? -eq 0 ];then
    log_str "INFO: the task was already running with [$pid]"
    exit 0
fi

if [ $START_FLAG -ne 1 ]; then
    log_str "INFO : Exit! please set \"start\" parameter if you want to start build"
    exit 0
fi

{
###begin
Run cd $ANDROID_ROOT
log_str "INFO: start ..."
[ -z "$COMBO_LIST" ] && COMBO_LIST=$DEFAULT_COMBO_LIST

###get repo status
log_str "INFO: get repo status ..."
repo_result=`repo status`
log_str "INFO: repo_result = $repo_result"

###backup the each changes
echo "$repo_result"|grep ^project|awk '{print $2}'|while read sub_dir
do
   mode_name=`echo $sub_dir|sed 's#/#_#g'`
   diff_name="${CURR_DATE}_${mode_name}.diff"
   Run cd $ANDROID_ROOT/$sub_dir
   git diff > $DIFF_SAVE/$diff_name
   echo "INFO: save $dir to $DIFF_SAVE/$diff_name"
   Run git reset --hard
   Run git clean -f
done

cd $ANDROID_ROOT
###sync to the lastest change
if [ $REPO_SYNC -eq 1 ];then
  log_str "INFO: start to repo sync..."
  repo sync -cdj4 --no-tags > $LOG_DIR/${CURR_DATE}_repo_sync.log 2>&1
  #repo sync -j4 > $LOG_DIR/${CURR_DATE}_repo_sync.log 2>&1
  [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
fi

###clean the out directory
if [ $CLEAN_OUT_DIRECTORY -eq 1 ];then
    log_str "clean out directory..."
    find -maxdepth 1 -type d -name "out.2*" -or -name out|xargs rm -fr {}
    [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
    log_str "mv -f out out.$CURR_DATE ..."
    mv -f out out.$CURR_DATE
    [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
fi

###compile the target
for i in ${COMBO_LIST[@]}
do
    log_file=$LOG_DIR/${CURR_DATE}_${i}.log
    log_str ""
    log_str "INFO: lunch $i ..."
    log_str "INFO: CPU_CORES=$CPU_CORES"
    cd $ANDROID_ROOT
    if [ -n "$EXTRA_CMD_BEFORE" ];then
        log_str "INFO: $EXTRA_CMD_BEFORE ...begin"
        echo "$EXTRA_CMD_BEFORE"|bash
        if [ $? -eq 0 ];then
            ret="OK"
        else
            ret="NOK"
        fi
        log_str "INFO: $EXTRA_CMD_BEFORE ...$ret"
    fi
    cd $ANDROID_ROOT
    . build/envsetup.sh > $log_file 2>&1
    lunch $i >> $log_file 2>&1

    MAX_TRY_COUNT=4
    try_i=0
    while [ $try_i -lt $MAX_TRY_COUNT ]
    do
        let "make_core=($CPU_CORES/$MAX_TRY_COUNT)*($MAX_TRY_COUNT-$try_i)"
        [ -z "$make_core" ] && make_core=1

        if [ $try_i -eq 0 ];then
            log_str "INFO: makeing $i [-j${make_core}]... "
        else
            log_str "INFO: makeing $i [-j${make_core}], try $try_i... "
        fi

        make -j${make_core} >> $log_file 2>&1

        if [ $? -eq 0 ];then
            log_str "INFO: OK"
            break
        else
            log_str "WARN: FAILED"
            no_space_err="No space left on device"
            if tail $log_file| grep "$no_space_err" 1>/dev/null 2>&1 ;then
                log_str "WARN: found the failed reason was \"$no_space_err\""
                log_str "INFO: clean out directory..."
                rm -fr out.2*
                [ $? -eq 0 ] && log_str "INFO: OK" || log_str "WARN: NOK"
            fi

            if [ $try_i -eq $MAX_TRY_COUNT ];then
                log_str "ERR : Already try $MAX_TRY_COUNT, both failed"
            fi
        fi
        let "try_i++"
    done
    if [ -n "$EXTRA_CMD_AFTER" ];then
        log_str "INFO: $EXTRA_CMD_AFTER ...begin"
        echo "$EXTRA_CMD_AFTER"|bash
        log_str "INFO: $EXTRA_CMD_AFTER ...finish"
    fi
done

log_str "INFO: finish"
} 1>/dev/null 2>&1 &
pid=$!
echo $pid > $PID_FILE
log_str "INFO: the task was running with [$pid]"








